Loading libraries:

library(topGO)
library(dplyr)
library(gridExtra)
library(Seurat)
library(velocyto.R)
library(SeuratWrappers)
library(ggplot2)
library(cowplot)
library(monocle3)

Loading the data:

load(file = 'hse.object.Rdata')
data <- sc.object

Subpopulations discovery

At first let’s do PCA:

data <- RunPCA(object = data, npcs = 50, verbose = FALSE)
ElbowPlot(data)

Seems like the majority of true signal is captured in the first 8 PCs.

Now let’s do clustering with resolution 0.2:

data <- FindNeighbors(object = data, dims = 1:50, verbose = FALSE)
data <- FindClusters(object = data, resolution = 0.2, verbose = FALSE)
table(Idents(data))

  0   1   2   3   4 
816 708 354 328  27 

We’ve found 5 clusters. Now let’s run t-SNE and UMAP to visualize these clusters:

data <- RunTSNE(object = data, dims = 1:50, verbose = FALSE)
data <- RunUMAP(object = data, dims = 1:50, verbose = FALSE)

Visualization:

PCAPlot(object = data, label = TRUE, label.size = 8)

TSNEPlot(object = data, label = TRUE, label.size = 8)

UMAPPlot(object = data, label = TRUE, label.size = 8)

Now let’s try with resolution 1.0:

data <- FindClusters(object = data, resolution = 1.0, verbose = FALSE)
table(Idents(data))

  0   1   2   3   4   5   6   7   8   9 
475 383 318 314 258 187 149 111  28  10 

We’ve obtained 10 clusters. Visualization:

PCAPlot(object = data, label = TRUE, label.size = 8)

TSNEPlot(object = data, label = TRUE, label.size = 8)

UMAPPlot(object = data, label = TRUE, label.size = 8)

Now let’s use cells field as identity for clustering:

Idents(data) <- 'cells'
PCAPlot(object = data, label = TRUE, label.size = 8)

TSNEPlot(object = data, label = TRUE, label.size = 8)

UMAPPlot(object = data, label = TRUE, label.size = 8)

So, we have 4 cell types in the field ‘cells’, 5 clusters in the first run of FindClusters() (resolution 0.2) and 10 clusters in the second run (resolution 1.0). And here’s the t-SNE plot of clusters (resolution 0.2) split by cell type:

Idents(data) <- 'integrated_snn_res.0.2'
TSNEPlot(object = data, label = TRUE, label.size = 8, split.by = 'cells')

DE of clustering and cells

Idents(data) <- 'integrated_snn_res.1'
table(Idents(data))

  0   1   2   3   4   5   6   7   8   9 
475 383 318 314 258 187 149 111  28  10 

Using the clustering obtained from running FindClusters() with resolution equal to 10:

dec10 <- FindAllMarkers(object = data, only.pos = TRUE, min.pct = 0.5, logfc.threshold = 0.2, verbose = FALSE)
top2_dec10 <- dec10 %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
DoHeatmap(object = data, features = top2_dec10$gene)

FeaturePlot(object = data, features = top2_dec10$gene, pt.size = 0.2, ncol = 2)

Switching back to cells and more different plots:

Idents(data) <- 'cells'
dec4 <- FindAllMarkers(object = data, only.pos = TRUE, min.pct = 0.5, logfc.threshold = 0.2, verbose = FALSE)
top2_dec4 <- dec4 %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
DoHeatmap(object = data, features = top2_dec4$gene)

FeaturePlot(object = data, features = top2_dec4$gene, pt.size = 0.2, ncol = 2)

VlnPlot(object = data, features = top2_dec4$gene, ncol = 2, pt.size = 0.1)

RidgePlot(object = data, features = top2_dec4$gene, ncol = 2)

DotPlot(object = data, features = top2_dec4$gene)

Also let’s perform GO enrichment analysis in cell types 1, 2 and 4 (ignoring 3 because of only 3 DE genes):

pval <- dec4$p_val_adj
names(pval) <- dec4$gene
gplots <- purrr::map(as.character(c(1, 2, 4)), function(cell) {
  gl <- pval[dec4$cluster == cell]
  setGO <- new('topGOdata', description = 'ns', ontology = 'BP',
             allGenes = gl, geneSel = function(x) return(x < 0.05),
             nodeSize = 10,
             annot = annFUN.org,
             mapping = 'org.Hs.eg.db',
             ID = 'symbol')
  resultKS.classic <- runTest(setGO, algorithm = 'classic', statistic = 'ks')
  goEnrichment <- GenTable(setGO, KS=resultKS.classic, orderBy = 'KS',
                         topNodes = 20)
  goEnrichment$ExtTerm <- paste(goEnrichment$GO.ID, goEnrichment$Term, sep = ', ')
  goEnrichment$KS <- as.numeric(gsub(',', '.', goEnrichment$KS))
  x <- ggplot(data = goEnrichment, aes(x = reorder(ExtTerm, -KS), y = -log(KS))) +
    geom_col(aes(fill = -log(KS)), na.rm = TRUE) + coord_flip() +
    scale_fill_gradient() +
    xlab('Enrichment') +
    ylab('Biological process') +
    ggtitle(paste('GO Enrichment in cell', cell)) +
    labs(fill = '-log(P-value)')
  return(x)
})
plot_grid(plotlist = gplots, ncol = 1)

Single cell article

A Single-Cell Transcriptome Atlas of the Aging Drosophila Brain

In the paper several different scRNA-seq datasets were prepared. 10x Genomics (Drop-Seq) was used for massive single-cell sequencing of all brain cells of fly (data sets DGRP-551 and \(w^{1118}\). The method is known for its highest throughput, but it suffers from low resolution and coverage. For identification of rare cell types the authors have used CEL-Seq2 and SMART-Seq2 methods which do not have performance combarable to Drop-Seq, but are more sensitive and give better coverage.

Drop-Seq data was processed using Cell Ranger pipeline (filtering, quality metrics, alignment). CEL-Seq2 cells were demultiplexed with scripts utilizing pysam and their cleaned (fastq-mcf) reads were aligned both with SMART-Seq2 reads using STAR.

The Seurat pipeline was used to perform normalization, cell clustering by means of SNN graph, t-SNE mapping and differential gene expression analysis. Across many other used computational methods stands out the NNLS regression used for cluster-to-bulk mapping which has shown high similarity between the single-cell and the bulk RNA-seq.

All the data has enabled the authors to claim some facts about gene regulation in fly brain cells which are in correspondence with amassed in previous publications, and integrate the knowledge into a SCope database.

Advanced task

Pseudotime trajectory

cds <- cds.graph
head(colData(cds))
DataFrame with 6 rows and 2 columns
                   original_type       Size_Factor
                       <numeric>         <numeric>
AACACGTTCTAGCACA-2             3  1.01351515312294
AACTCCCTCAGTTGAC-2             3  2.36441445413701
ACACCGGCATTGCGGC-2             3 0.758291029129238
ACGAGCCAGAGGTAGA-2             3  1.57733618788545
ACGGAGATCAAACAAG-2             3 0.402964540152018
ACTATCTAGCTGAACG-2             3  1.78696632487873

There is no info about batches, so no way to account for batch effect.

cds <- preprocess_cds(cds = cds, num_dim = 50)
plot_pc_variance_explained(cds)

cds <- reduce_dimension(cds = cds, reduction_method = 'UMAP', preprocess_method = 'PCA')
plot_cells(cds, color_cells_by = 'original_type', group_label_size = 4)

Clustering cells:

cds <- cluster_cells(cds = cds)
plot_cells(cds, color_cells_by = 'partition', group_label_size = 4)

Learning the trajectory graph:

cds <- learn_graph(cds = cds)
plot_cells(cds = cds, color_cells_by = 'original_type', graph_label_size = 4)

Ordering the cells:

cds <- order_cells(cds = cds, reduction_method = 'UMAP')
plot_cells(cds = cds, color_cells_by = 'pseudotime', graph_label_size = 4)

Genes changing along the trajectory:

traj_genes <- graph_test(cds = cds, neighbor_graph = 'principal_graph', cores = 8,
                         verbose = FALSE)
arr_traj_genes <- arrange(traj_genes, q_value)
head(arr_traj_genes[, 5:6], 10)
plot_cells(cds = cds, genes = arr_traj_genes$gene_short_name[1:6], 
           show_trajectory_graph = FALSE, label_cell_groups = FALSE, 
           label_leaves = FALSE)

Collecting the trajectory-variable genes into modules (plot is the aggregate module scores within each cell type):

gene_modules <- find_gene_modules(cds[traj_genes$q_value < 0.05 & !is.na(traj_genes$q_value), ])
cell_groups <- tibble(cell_id = rownames(colData(cds)),
                      cell_group = colData(cds)$original_type)
agg_mat <- aggregate_gene_expression(cds = cds, gene_group_df = gene_modules, 
                                     cell_group_df = cell_groups)
rownames(agg_mat) <- paste0('Module ', rownames(agg_mat))
pheatmap::pheatmap(mat = agg_mat, scale = 'column', clustering_method = 'ward.D2')

Plot expression across first 4 modules:

plot_cells(cds = cds, genes = gene_modules[gene_modules$module %in% 1:4, ],
           label_cell_groups = FALSE,
           show_trajectory_graph = FALSE)

Analysis of cellular dynamics using RNA velocity

bm <- SCTransform(object = bm.integ, assay = 'spliced', verbose = FALSE)
bm <- RunPCA(object = bm, verbose = FALSE)
bm <- FindNeighbors(object = bm, dims = 1:50, verbose = FALSE)
bm <- FindClusters(object = bm, verbose = FALSE)
bm <- RunUMAP(object = bm, dims = 1:50, verbose = FALSE)
bm <- RunVelocity(object = bm, deltaT = 1, kCells = 25, fit.quantile = 0.02, 
                  verbose = FALSE, ncores = 6)
colors <- (scales::hue_pal())(length(levels(bm)))
names(colors) <- levels(bm)
cell_colors <- colors[Idents(bm)]
names(cell_colors) <- colnames(bm)
show.velocity.on.embedding.cor(
  emb = Embeddings(object = bm, reduction = 'umap'),
  vel = Tool(object = bm, slot = 'RunVelocity'),
  n = 200,  scale = 'sqrt',
  cell.colors = ac(x = cell_colors, alpha = 0.5),
  cex = 0.8, arrow.scale = 3, show.grid.flow = TRUE,
  min.grid.cell.mass = 0.5, grid.n = 40, arrow.lwd = 1, 
  do.par = FALSE, cell.border.alpha = 0.1
)

LS0tCnRpdGxlOiAic2NSTkEtc2VxIFJlcG9ydCIKYXV0aG9yOiAiTGV2IE1hemFldiIKZGF0ZTogIk5vdmVtYmVyIDEsIDIwMTkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKTG9hZGluZyBsaWJyYXJpZXM6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0b3BHTykKbGlicmFyeShkcGx5cikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHZlbG9jeXRvLlIpCmxpYnJhcnkoU2V1cmF0V3JhcHBlcnMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KG1vbm9jbGUzKQpgYGAKCkxvYWRpbmcgdGhlIGRhdGE6CgpgYGB7cn0KbG9hZChmaWxlID0gJ2hzZS5vYmplY3QuUmRhdGEnKQpkYXRhIDwtIHNjLm9iamVjdApgYGAKCiMjIyBTdWJwb3B1bGF0aW9ucyBkaXNjb3ZlcnkKCkF0IGZpcnN0IGxldCdzIGRvIFBDQToKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpkYXRhIDwtIFJ1blBDQShvYmplY3QgPSBkYXRhLCBucGNzID0gNTAsIHZlcmJvc2UgPSBGQUxTRSkKRWxib3dQbG90KGRhdGEpCmBgYAoKU2VlbXMgbGlrZSB0aGUgbWFqb3JpdHkgb2YgdHJ1ZSBzaWduYWwgaXMgY2FwdHVyZWQgaW4gdGhlIGZpcnN0IDggUENzLgoKTm93IGxldCdzIGRvIGNsdXN0ZXJpbmcgd2l0aCByZXNvbHV0aW9uIDAuMjoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhIDwtIEZpbmROZWlnaGJvcnMob2JqZWN0ID0gZGF0YSwgZGltcyA9IDE6NTAsIHZlcmJvc2UgPSBGQUxTRSkKZGF0YSA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gZGF0YSwgcmVzb2x1dGlvbiA9IDAuMiwgdmVyYm9zZSA9IEZBTFNFKQp0YWJsZShJZGVudHMoZGF0YSkpCmBgYAoKV2UndmUgZm91bmQgNSBjbHVzdGVycy4gTm93IGxldCdzIHJ1biB0LVNORSBhbmQgVU1BUCB0byB2aXN1YWxpemUgdGhlc2UgY2x1c3RlcnM6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YSA8LSBSdW5UU05FKG9iamVjdCA9IGRhdGEsIGRpbXMgPSAxOjUwLCB2ZXJib3NlID0gRkFMU0UpCmRhdGEgPC0gUnVuVU1BUChvYmplY3QgPSBkYXRhLCBkaW1zID0gMTo1MCwgdmVyYm9zZSA9IEZBTFNFKQpgYGAKClZpc3VhbGl6YXRpb246CgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KUENBUGxvdChvYmplY3QgPSBkYXRhLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA4KQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpUU05FUGxvdChvYmplY3QgPSBkYXRhLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA4KQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpVTUFQUGxvdChvYmplY3QgPSBkYXRhLCBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA4KQpgYGAKCk5vdyBsZXQncyB0cnkgd2l0aCByZXNvbHV0aW9uIDEuMDoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhIDwtIEZpbmRDbHVzdGVycyhvYmplY3QgPSBkYXRhLCByZXNvbHV0aW9uID0gMS4wLCB2ZXJib3NlID0gRkFMU0UpCnRhYmxlKElkZW50cyhkYXRhKSkKYGBgCgpXZSd2ZSBvYnRhaW5lZCAxMCBjbHVzdGVycy4gVmlzdWFsaXphdGlvbjoKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpQQ0FQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClRTTkVQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClVNQVBQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKTm93IGxldCdzIHVzZSBjZWxscyBmaWVsZCBhcyBpZGVudGl0eSBmb3IgY2x1c3RlcmluZzoKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpJZGVudHMoZGF0YSkgPC0gJ2NlbGxzJwpQQ0FQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClRTTkVQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9ClVNQVBQbG90KG9iamVjdCA9IGRhdGEsIGxhYmVsID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDgpCmBgYAoKU28sIHdlIGhhdmUgNCBjZWxsIHR5cGVzIGluIHRoZSBmaWVsZCAnY2VsbHMnLCA1IGNsdXN0ZXJzIGluIHRoZSBmaXJzdCBydW4gb2YgYEZpbmRDbHVzdGVycygpYCAocmVzb2x1dGlvbiAwLjIpIGFuZCAxMCBjbHVzdGVycyBpbiB0aGUgc2Vjb25kIHJ1biAocmVzb2x1dGlvbiAxLjApLiBBbmQgaGVyZSdzIHRoZSB0LVNORSBwbG90IG9mIGNsdXN0ZXJzIChyZXNvbHV0aW9uIDAuMikgc3BsaXQgYnkgY2VsbCB0eXBlOgoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9CklkZW50cyhkYXRhKSA8LSAnaW50ZWdyYXRlZF9zbm5fcmVzLjAuMicKVFNORVBsb3Qob2JqZWN0ID0gZGF0YSwgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gOCwgc3BsaXQuYnkgPSAnY2VsbHMnKQpgYGAKCiMjIyBERSBvZiBjbHVzdGVyaW5nIGFuZCBjZWxscwoKYGBge3J9CklkZW50cyhkYXRhKSA8LSAnaW50ZWdyYXRlZF9zbm5fcmVzLjEnCnRhYmxlKElkZW50cyhkYXRhKSkKYGBgCgpVc2luZyB0aGUgY2x1c3RlcmluZyBvYnRhaW5lZCBmcm9tIHJ1bm5pbmcgYEZpbmRDbHVzdGVycygpYCB3aXRoIHJlc29sdXRpb24gZXF1YWwgdG8gMTA6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9NiwgcmVzdWx0cz0naGlkZScsIGZpZy5rZWVwPSdhbGwnfQpkZWMxMCA8LSBGaW5kQWxsTWFya2VycyhvYmplY3QgPSBkYXRhLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgdmVyYm9zZSA9IEZBTFNFKQp0b3AyX2RlYzEwIDwtIGRlYzEwICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDIsIHd0ID0gYXZnX2xvZ0ZDKQpEb0hlYXRtYXAob2JqZWN0ID0gZGF0YSwgZmVhdHVyZXMgPSB0b3AyX2RlYzEwJGdlbmUpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTE4fQpGZWF0dXJlUGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjMTAkZ2VuZSwgcHQuc2l6ZSA9IDAuMiwgbmNvbCA9IDIpCmBgYAoKU3dpdGNoaW5nIGJhY2sgdG8gY2VsbHMgYW5kIG1vcmUgZGlmZmVyZW50IHBsb3RzOgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTYsIHJlc3VsdHM9J2hpZGUnLCBmaWcua2VlcD0nYWxsJ30KSWRlbnRzKGRhdGEpIDwtICdjZWxscycKZGVjNCA8LSBGaW5kQWxsTWFya2VycyhvYmplY3QgPSBkYXRhLCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgdmVyYm9zZSA9IEZBTFNFKQp0b3AyX2RlYzQgPC0gZGVjNCAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG4gPSAyLCB3dCA9IGF2Z19sb2dGQykKRG9IZWF0bWFwKG9iamVjdCA9IGRhdGEsIGZlYXR1cmVzID0gdG9wMl9kZWM0JGdlbmUpCmBgYAoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEyfQpGZWF0dXJlUGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjNCRnZW5lLCBwdC5zaXplID0gMC4yLCBuY29sID0gMikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9MTJ9ClZsblBsb3Qob2JqZWN0ID0gZGF0YSwgZmVhdHVyZXMgPSB0b3AyX2RlYzQkZ2VuZSwgbmNvbCA9IDIsIHB0LnNpemUgPSAwLjEpCmBgYAoKYGBge3IgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9ClJpZGdlUGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjNCRnZW5lLCBuY29sID0gMikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KRG90UGxvdChvYmplY3QgPSBkYXRhLCBmZWF0dXJlcyA9IHRvcDJfZGVjNCRnZW5lKQpgYGAKCkFsc28gbGV0J3MgcGVyZm9ybSBHTyBlbnJpY2htZW50IGFuYWx5c2lzIGluIGNlbGwgdHlwZXMgMSwgMiBhbmQgNCAoaWdub3JpbmcgMyBiZWNhdXNlIG9mIG9ubHkgMyBERSBnZW5lcyk6CgpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnB2YWwgPC0gZGVjNCRwX3ZhbF9hZGoKbmFtZXMocHZhbCkgPC0gZGVjNCRnZW5lCmdwbG90cyA8LSBwdXJycjo6bWFwKGFzLmNoYXJhY3RlcihjKDEsIDIsIDQpKSwgZnVuY3Rpb24oY2VsbCkgewogIGdsIDwtIHB2YWxbZGVjNCRjbHVzdGVyID09IGNlbGxdCiAgc2V0R08gPC0gbmV3KCd0b3BHT2RhdGEnLCBkZXNjcmlwdGlvbiA9ICducycsIG9udG9sb2d5ID0gJ0JQJywKICAgICAgICAgICAgIGFsbEdlbmVzID0gZ2wsIGdlbmVTZWwgPSBmdW5jdGlvbih4KSByZXR1cm4oeCA8IDAuMDUpLAogICAgICAgICAgICAgbm9kZVNpemUgPSAxMCwKICAgICAgICAgICAgIGFubm90ID0gYW5uRlVOLm9yZywKICAgICAgICAgICAgIG1hcHBpbmcgPSAnb3JnLkhzLmVnLmRiJywKICAgICAgICAgICAgIElEID0gJ3N5bWJvbCcpCiAgcmVzdWx0S1MuY2xhc3NpYyA8LSBydW5UZXN0KHNldEdPLCBhbGdvcml0aG0gPSAnY2xhc3NpYycsIHN0YXRpc3RpYyA9ICdrcycpCiAgZ29FbnJpY2htZW50IDwtIEdlblRhYmxlKHNldEdPLCBLUz1yZXN1bHRLUy5jbGFzc2ljLCBvcmRlckJ5ID0gJ0tTJywKICAgICAgICAgICAgICAgICAgICAgICAgIHRvcE5vZGVzID0gMjApCiAgZ29FbnJpY2htZW50JEV4dFRlcm0gPC0gcGFzdGUoZ29FbnJpY2htZW50JEdPLklELCBnb0VucmljaG1lbnQkVGVybSwgc2VwID0gJywgJykKICBnb0VucmljaG1lbnQkS1MgPC0gYXMubnVtZXJpYyhnc3ViKCcsJywgJy4nLCBnb0VucmljaG1lbnQkS1MpKQogIHggPC0gZ2dwbG90KGRhdGEgPSBnb0VucmljaG1lbnQsIGFlcyh4ID0gcmVvcmRlcihFeHRUZXJtLCAtS1MpLCB5ID0gLWxvZyhLUykpKSArCiAgICBnZW9tX2NvbChhZXMoZmlsbCA9IC1sb2coS1MpKSwgbmEucm0gPSBUUlVFKSArIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KCkgKwogICAgeGxhYignRW5yaWNobWVudCcpICsKICAgIHlsYWIoJ0Jpb2xvZ2ljYWwgcHJvY2VzcycpICsKICAgIGdndGl0bGUocGFzdGUoJ0dPIEVucmljaG1lbnQgaW4gY2VsbCcsIGNlbGwpKSArCiAgICBsYWJzKGZpbGwgPSAnLWxvZyhQLXZhbHVlKScpCiAgcmV0dXJuKHgpCn0pCnBsb3RfZ3JpZChwbG90bGlzdCA9IGdwbG90cywgbmNvbCA9IDEpCmBgYAoKIyMjIFNpbmdsZSBjZWxsIGFydGljbGUKCioqW0EgU2luZ2xlLUNlbGwgVHJhbnNjcmlwdG9tZSBBdGxhcyBvZiB0aGUgQWdpbmcgRHJvc29waGlsYSBCcmFpbl0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNjA4NjkzNS8pKioKCkluIHRoZSBwYXBlciBzZXZlcmFsIGRpZmZlcmVudCBzY1JOQS1zZXEgZGF0YXNldHMgd2VyZSBwcmVwYXJlZC4gMTB4IEdlbm9taWNzIChEcm9wLVNlcSkgd2FzIHVzZWQgZm9yIG1hc3NpdmUgc2luZ2xlLWNlbGwgc2VxdWVuY2luZyBvZiBhbGwgYnJhaW4gY2VsbHMgb2YgZmx5IChkYXRhIHNldHMgREdSUC01NTEgYW5kICR3XnsxMTE4fSQuIFRoZSBtZXRob2QgaXMga25vd24gZm9yIGl0cyBoaWdoZXN0IHRocm91Z2hwdXQsIGJ1dCBpdCBzdWZmZXJzIGZyb20gbG93IHJlc29sdXRpb24gYW5kIGNvdmVyYWdlLiBGb3IgaWRlbnRpZmljYXRpb24gb2YgcmFyZSBjZWxsIHR5cGVzIHRoZSBhdXRob3JzIGhhdmUgdXNlZCBDRUwtU2VxMiBhbmQgU01BUlQtU2VxMiBtZXRob2RzIHdoaWNoIGRvIG5vdCBoYXZlIHBlcmZvcm1hbmNlIGNvbWJhcmFibGUgdG8gRHJvcC1TZXEsIGJ1dCBhcmUgbW9yZSBzZW5zaXRpdmUgYW5kIGdpdmUgYmV0dGVyIGNvdmVyYWdlLgoKRHJvcC1TZXEgZGF0YSB3YXMgcHJvY2Vzc2VkIHVzaW5nIENlbGwgUmFuZ2VyIHBpcGVsaW5lIChmaWx0ZXJpbmcsIHF1YWxpdHkgbWV0cmljcywgYWxpZ25tZW50KS4gQ0VMLVNlcTIgY2VsbHMgd2VyZSBkZW11bHRpcGxleGVkIHdpdGggc2NyaXB0cyB1dGlsaXppbmcgcHlzYW0gYW5kIHRoZWlyIGNsZWFuZWQgKGZhc3RxLW1jZikgcmVhZHMgd2VyZSBhbGlnbmVkIGJvdGggd2l0aCBTTUFSVC1TZXEyIHJlYWRzIHVzaW5nIFNUQVIuIAoKVGhlIFNldXJhdCBwaXBlbGluZSB3YXMgdXNlZCB0byBwZXJmb3JtIG5vcm1hbGl6YXRpb24sIGNlbGwgY2x1c3RlcmluZyBieSBtZWFucyBvZiBTTk4gZ3JhcGgsIHQtU05FIG1hcHBpbmcgYW5kIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuIEFjcm9zcyBtYW55IG90aGVyIHVzZWQgY29tcHV0YXRpb25hbCBtZXRob2RzIHN0YW5kcyBvdXQgdGhlIE5OTFMgcmVncmVzc2lvbiB1c2VkIGZvciBjbHVzdGVyLXRvLWJ1bGsgbWFwcGluZyB3aGljaCBoYXMgc2hvd24gaGlnaCBzaW1pbGFyaXR5IGJldHdlZW4gdGhlIHNpbmdsZS1jZWxsIGFuZCB0aGUgYnVsayBSTkEtc2VxLgoKQWxsIHRoZSBkYXRhIGhhcyBlbmFibGVkIHRoZSBhdXRob3JzIHRvIGNsYWltIHNvbWUgZmFjdHMgYWJvdXQgZ2VuZSByZWd1bGF0aW9uIGluIGZseSBicmFpbiBjZWxscyB3aGljaCBhcmUgaW4gY29ycmVzcG9uZGVuY2Ugd2l0aCBhbWFzc2VkIGluIHByZXZpb3VzIHB1YmxpY2F0aW9ucywgYW5kIGludGVncmF0ZSB0aGUga25vd2xlZGdlIGludG8gYSBTQ29wZSBkYXRhYmFzZS4KCiMjIEFkdmFuY2VkIHRhc2sKCiMjIyBQc2V1ZG90aW1lIHRyYWplY3RvcnkKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjZHMgPC0gY2RzLmdyYXBoCmhlYWQoY29sRGF0YShjZHMpKQpgYGAKClRoZXJlIGlzIG5vIGluZm8gYWJvdXQgYmF0Y2hlcywgc28gbm8gd2F5IHRvIGFjY291bnQgZm9yIGJhdGNoIGVmZmVjdC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpjZHMgPC0gcHJlcHJvY2Vzc19jZHMoY2RzID0gY2RzLCBudW1fZGltID0gNTApCnBsb3RfcGNfdmFyaWFuY2VfZXhwbGFpbmVkKGNkcykKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KY2RzIDwtIHJlZHVjZV9kaW1lbnNpb24oY2RzID0gY2RzLCByZWR1Y3Rpb25fbWV0aG9kID0gJ1VNQVAnLCBwcmVwcm9jZXNzX21ldGhvZCA9ICdQQ0EnKQpwbG90X2NlbGxzKGNkcywgY29sb3JfY2VsbHNfYnkgPSAnb3JpZ2luYWxfdHlwZScsIGdyb3VwX2xhYmVsX3NpemUgPSA0KQpgYGAKCkNsdXN0ZXJpbmcgY2VsbHM6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KY2RzIDwtIGNsdXN0ZXJfY2VsbHMoY2RzID0gY2RzKQpwbG90X2NlbGxzKGNkcywgY29sb3JfY2VsbHNfYnkgPSAncGFydGl0aW9uJywgZ3JvdXBfbGFiZWxfc2l6ZSA9IDQpCmBgYAoKTGVhcm5pbmcgdGhlIHRyYWplY3RvcnkgZ3JhcGg6CgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9NiwgcmVzdWx0cz0naGlkZScsIGZpZy5rZWVwPSdhbGwnfQpjZHMgPC0gbGVhcm5fZ3JhcGgoY2RzID0gY2RzKQpwbG90X2NlbGxzKGNkcyA9IGNkcywgY29sb3JfY2VsbHNfYnkgPSAnb3JpZ2luYWxfdHlwZScsIGdyYXBoX2xhYmVsX3NpemUgPSA0KQpgYGAKCk9yZGVyaW5nIHRoZSBjZWxsczoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpjZHMgPC0gb3JkZXJfY2VsbHMoY2RzID0gY2RzLCByZWR1Y3Rpb25fbWV0aG9kID0gJ1VNQVAnKQpwbG90X2NlbGxzKGNkcyA9IGNkcywgY29sb3JfY2VsbHNfYnkgPSAncHNldWRvdGltZScsIGdyYXBoX2xhYmVsX3NpemUgPSA0KQpgYGAKCkdlbmVzIGNoYW5naW5nIGFsb25nIHRoZSB0cmFqZWN0b3J5OgoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTksIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQp0cmFqX2dlbmVzIDwtIGdyYXBoX3Rlc3QoY2RzID0gY2RzLCBuZWlnaGJvcl9ncmFwaCA9ICdwcmluY2lwYWxfZ3JhcGgnLCBjb3JlcyA9IDgsCiAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpCmFycl90cmFqX2dlbmVzIDwtIGFycmFuZ2UodHJhal9nZW5lcywgcV92YWx1ZSkKYGBgCgpgYGB7cn0KaGVhZChhcnJfdHJhal9nZW5lc1ssIDU6Nl0sIDEwKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGxvdF9jZWxscyhjZHMgPSBjZHMsIGdlbmVzID0gYXJyX3RyYWpfZ2VuZXMkZ2VuZV9zaG9ydF9uYW1lWzE6Nl0sIAogICAgICAgICAgIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IEZBTFNFLCBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFLCAKICAgICAgICAgICBsYWJlbF9sZWF2ZXMgPSBGQUxTRSkKYGBgCgpDb2xsZWN0aW5nIHRoZSB0cmFqZWN0b3J5LXZhcmlhYmxlIGdlbmVzIGludG8gbW9kdWxlcyAocGxvdCBpcyB0aGUgYWdncmVnYXRlIG1vZHVsZSBzY29yZXMgd2l0aGluIGVhY2ggY2VsbCB0eXBlKToKCmBgYHtyfQpnZW5lX21vZHVsZXMgPC0gZmluZF9nZW5lX21vZHVsZXMoY2RzW3RyYWpfZ2VuZXMkcV92YWx1ZSA8IDAuMDUgJiAhaXMubmEodHJhal9nZW5lcyRxX3ZhbHVlKSwgXSkKY2VsbF9ncm91cHMgPC0gdGliYmxlKGNlbGxfaWQgPSByb3duYW1lcyhjb2xEYXRhKGNkcykpLAogICAgICAgICAgICAgICAgICAgICAgY2VsbF9ncm91cCA9IGNvbERhdGEoY2RzKSRvcmlnaW5hbF90eXBlKQphZ2dfbWF0IDwtIGFnZ3JlZ2F0ZV9nZW5lX2V4cHJlc3Npb24oY2RzID0gY2RzLCBnZW5lX2dyb3VwX2RmID0gZ2VuZV9tb2R1bGVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfZ3JvdXBfZGYgPSBjZWxsX2dyb3VwcykKcm93bmFtZXMoYWdnX21hdCkgPC0gcGFzdGUwKCdNb2R1bGUgJywgcm93bmFtZXMoYWdnX21hdCkpCnBoZWF0bWFwOjpwaGVhdG1hcChtYXQgPSBhZ2dfbWF0LCBzY2FsZSA9ICdjb2x1bW4nLCBjbHVzdGVyaW5nX21ldGhvZCA9ICd3YXJkLkQyJykKYGBgCgpQbG90IGV4cHJlc3Npb24gYWNyb3NzIGZpcnN0IDQgbW9kdWxlczoKCmBgYHtyLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpwbG90X2NlbGxzKGNkcyA9IGNkcywgZ2VuZXMgPSBnZW5lX21vZHVsZXNbZ2VuZV9tb2R1bGVzJG1vZHVsZSAlaW4lIDE6NCwgXSwKICAgICAgICAgICBsYWJlbF9jZWxsX2dyb3VwcyA9IEZBTFNFLAogICAgICAgICAgIHNob3dfdHJhamVjdG9yeV9ncmFwaCA9IEZBTFNFKQpgYGAKCiMjIyBBbmFseXNpcyBvZiBjZWxsdWxhciBkeW5hbWljcyB1c2luZyBSTkEgdmVsb2NpdHkKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpibSA8LSBTQ1RyYW5zZm9ybShvYmplY3QgPSBibS5pbnRlZywgYXNzYXkgPSAnc3BsaWNlZCcsIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0Kcm0obGlzdCA9IGMoJ2JtLmludGVnJywgJ2NkcycsICdjZHMuZ3JhcGgnLCAnc2Mub2JqZWN0JywgJ2RhdGEnKSkKYGBgCgpgYGB7cn0KYm0gPC0gUnVuUENBKG9iamVjdCA9IGJtLCB2ZXJib3NlID0gRkFMU0UpCmBgYAoKYGBge3J9CmJtIDwtIEZpbmROZWlnaGJvcnMob2JqZWN0ID0gYm0sIGRpbXMgPSAxOjUwLCB2ZXJib3NlID0gRkFMU0UpCmJtIDwtIEZpbmRDbHVzdGVycyhvYmplY3QgPSBibSwgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpibSA8LSBSdW5VTUFQKG9iamVjdCA9IGJtLCBkaW1zID0gMTo1MCwgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KYm0gPC0gUnVuVmVsb2NpdHkob2JqZWN0ID0gYm0sIGRlbHRhVCA9IDEsIGtDZWxscyA9IDI1LCBmaXQucXVhbnRpbGUgPSAwLjAyLCAKICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLCBuY29yZXMgPSA2KQpgYGAKCmBgYHtyfQpjb2xvcnMgPC0gKHNjYWxlczo6aHVlX3BhbCgpKShsZW5ndGgobGV2ZWxzKGJtKSkpCm5hbWVzKGNvbG9ycykgPC0gbGV2ZWxzKGJtKQpjZWxsX2NvbG9ycyA8LSBjb2xvcnNbSWRlbnRzKGJtKV0KbmFtZXMoY2VsbF9jb2xvcnMpIDwtIGNvbG5hbWVzKGJtKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02LCByZXN1bHRzPSdoaWRlJywgZmlnLmtlZXA9J2FsbCd9CnNob3cudmVsb2NpdHkub24uZW1iZWRkaW5nLmNvcigKICBlbWIgPSBFbWJlZGRpbmdzKG9iamVjdCA9IGJtLCByZWR1Y3Rpb24gPSAndW1hcCcpLAogIHZlbCA9IFRvb2wob2JqZWN0ID0gYm0sIHNsb3QgPSAnUnVuVmVsb2NpdHknKSwKICBuID0gMjAwLCAgc2NhbGUgPSAnc3FydCcsCiAgY2VsbC5jb2xvcnMgPSBhYyh4ID0gY2VsbF9jb2xvcnMsIGFscGhhID0gMC41KSwKICBjZXggPSAwLjgsIGFycm93LnNjYWxlID0gMywgc2hvdy5ncmlkLmZsb3cgPSBUUlVFLAogIG1pbi5ncmlkLmNlbGwubWFzcyA9IDAuNSwgZ3JpZC5uID0gNDAsIGFycm93Lmx3ZCA9IDEsIAogIGRvLnBhciA9IEZBTFNFLCBjZWxsLmJvcmRlci5hbHBoYSA9IDAuMQopCmBgYAo=